#!/usr/bin/php
<?php

/**
 *
 * @param string $type
 * @return string
 */
function jstype($type)
{
    switch ($type) {
        case 'bit':
            $type = 'Boolean';
            break;

        case 'octet':
        case 'short':
        case 'long':
        case 'longlong':
            $type = 'Number';
            break;

        case 'table':
            $type = 'Object';
            break;

        case 'shortstr':
        case 'longstr':
            $type = 'String';
            break;

        case 'timestamp':
            $type = 'Date';
            break;

        default:
            $type = 'String';
    }
    return $type;
}
/**
 *
 * @param string $str
 * @param integer $p start segment
 * @param string $s separator
 * @return string
 */
function camelize($str, $p = 0, $s = ' ')
{
    $str = explode($s, $str);
    for ($i = $p, $l = sizeof($str); $i < $l; ++ $i) {
        $str[$i] = ucfirst($str[$i]);
    }
    return implode('', $str);

}

/**
 *
 * @param integer $i
 * @param string $t
 * @return string
 */
function tab($i = 1, $t = '    ')
{
    return str_repeat($t, $i);
}

/**
 *
 * @param integer $i
 * @return string
 */
function nl($i = 1)
{
    return str_repeat("\n", 1);
}

$options = getopt('amqp');
if (!isset($options['amqp']) || !$options['amqp']) {
    $options['amqp'] = '0-8';
}

$xml = simplexml_load_file('../xml/amqp' . $options['amqp'] . '.xml');

/* VERSION */
$jsVersion = <<< EOT
var Version = {
    Major: {$xml['major']},
    Minor: {$xml['minor']}
};

EOT;

/* CONSTANTS */

$jsConsts = <<< EOT
var Const = {
//%const
};

EOT;
$buffer = array();
foreach ($xml->constant as $constant) {
    $name = camelize($constant['name']);
    $buffer[] = <<< EOT
    {$name}: {$constant['value']}
EOT;
}
$buffer = implode(',' . nl(), $buffer);
$jsConsts =  str_replace('//%const', $buffer, $jsConsts);

/* CLASSES METHODS */

$jsClasses = 'var Class = {' . nl();
$jsClasses2 = '';
$classBuffer = array();
foreach ($xml->class as $class) {
    $className = camelize($class['name']);
    $classId = $class['index'];

    $jsClass = <<< EOT
    {$classId}: {
//%fields
        /** @ignore */
        toString: function(){ return '{$className}'; },

EOT;
    $jsClasses2 .= ',' . nl() . tab(1) . "{$className}: {" . nl() . tab(2) . "toString: function(){ return {$classId}; },";


    $fields = '';
    $fieldNameBuffer = array();
    $fieldTypeBuffer = array();
    foreach ($class->field as $field) {
        if ($field['domain']) {
            $domain = $xml->xpath('/amqp/domain[@name="' . $field['domain'] . '"]');
            $type = $domain[0]['type'];
        } else {
            $type = $field['type'];
        }
        $name = camelize($field['name'], 1);
        $fieldNameBuffer[] = $name;
        $fieldTypeBuffer[] = $type;
    }
    if ($fieldNameBuffer) {
        $fieldsNames = "'" . implode("', '", $fieldNameBuffer) . "'";
        $fieldsTypes = "'" . implode("', '", $fieldTypeBuffer) . "'";
        $fields = <<< EOT
        fields: {
            enum: [{$fieldsNames}],
            type: [{$fieldsTypes}]
        },
EOT;
    }
    $jsClass = str_replace('//%fields', $fields, $jsClass);


    $methodBuffer = array();
    $methodBuffer2 = array();
    foreach ($class->method as $method) {
        $methodName = camelize($method['name'], 0, '-');
        $methodId = $method['index'];
        $jsMethod = <<< EOT
        {$methodId}: {
            /** @ignore */
            toString: function(){ return '{$methodName}'; },

EOT;
        $methodQueueBuffer = array();
        $methodTypesBuffer = array();
        foreach ($method->field as $field) {
            $name = camelize($field['name'], 1);
            if ($field['domain']) {
                $xpath = '/amqp/domain[@name="' . $field['domain'] . '"]';
                $domain = $xml->xpath($xpath);
                $type = $domain[0]['type'];
            } else {
                $type = $field['type'];
            }
            $methodQueueBuffer[] = "'{$name}'";
            $methodTypesBuffer[] = "'{$type}'";
        }
        $enum = implode(', ', $methodQueueBuffer);
        $type = implode(', ', $methodTypesBuffer);
        $jsMethod .= <<< EOT
            enum: [{$enum}],
            type: [{$type}]
        }
EOT;
        $methodBuffer[] = $jsMethod;
        $methodBuffer2[] =  nl() . tab(2) . "{$methodName}: {$methodId}";
    }
    $jsClasses2 .= implode(',', $methodBuffer2) . nl() . tab() . '}';
    $jsClass .= implode(', ' . nl(), $methodBuffer) . nl() . tab(1) . '}';
    $classBuffer[] = $jsClass;
}
$jsClasses .= implode(', ' . nl(), $classBuffer) . $jsClasses2 . nl() . '};' . nl();

$jsExports = 'exports.Version = Version;' . nl() . 'exports.Const = Const;' . nl() . 'exports.Class = Class;' . nl();

/* WRITE */

$fp = fopen('../src/api/amqp.js', 'w');
fwrite($fp, $jsVersion);
fwrite($fp, $jsConsts);
fwrite($fp, $jsClasses);
fwrite($fp, $jsExports);
fclose($fp);


/* GENERATE CLASSES */

foreach ($xml->class as $class) {
    $className = camelize($class['name']);
    $class2 = strtolower($className);
    $classId = $class['index'];

    $shortDoc = ucfirst(trim($class));
    $doc = trim($class->doc);
    $doc = str_replace("\n", "\n * ", $doc);
    $handler = ucfirst(trim($class['handler']));

    $classBody = <<< EOT
/**
 * {$shortDoc}.
 *
 * {$doc}
 *
 * @name {$className}
 * @exports {$className} as api.{$className}
 * @class
 */

//%events
//%fields
//%methods

EOT;

    $eventHandlerBody = <<< EOT
//%events
EOT;

    $classFields = '';
    foreach ($class->field as $field) {
        $fieldName = camelize($field['name'], 1);
        if ($field['domain']) {
            $xpath = '/amqp/domain[@name="' . $field['domain'] . '"]';
            $domain = $xml->xpath($xpath);
            $type = $domain[0]['type'];
            $doc = trim($domain[0]->doc);
        } else {
            $type = $field['type'];
            $doc = trim($field);
        }
        $fieldType = jstype($type);
        $classFields .= <<< EOT
/**
 * {$doc}
 *
 * @name {$className}#{$fieldName}
 * @field
 * @type {$fieldType}
 */

EOT;
    }

    $classMethods = '';
    $events = array();
    foreach ($class->method as $method) {
        $clientChassis = $method->xpath('chassis[@name="client"]');
        $serverChassis = $method->xpath('chassis[@name="server"]');

        if ($clientChassis[0]) {
            $params = array();
            foreach ($method->field as $field) {
                $fieldName = camelize($field['name'], 1, ' ');

                if ($field['domain']) {
                    $xpath = '/amqp/domain[@name="' . $field['domain'] . '"]';
                    $domain = $xml->xpath($xpath);
                    $type = $domain[0]['type'];
                    $doc = trim($domain[0]->doc);
                } else {
                    $type = $field['type'];
                    $doc = trim($field->doc);
                }
                $doc = str_replace("\n", "\n * ", $doc);
                $type = jstype($type);
                $params[] = "@param {{$type}} {$fieldName} {$doc}";
            }
            $doc = trim($method->doc);
            $doc = str_replace("\n", "\n * ", $doc);
            $params = implode(nl() . ' * ', $params);
            $events[] = array(
                'code' => $method['index'],
                'name' => camelize($method['name'], 1, '-'),
                'doc' => $doc,
                'short' => ucfirst(trim($method)),
                'params' => $params,
            );
        }
        if (!$serverChassis) {
            continue;
        }

        $methodName = camelize($method['name'], 1, '-');
        $methodId = $method['index'];

        if ('return' == $methodName || 'delete' == $methodName) {
            $_methodName = "['{$methodName}']";
        } else {
            $_methodName = ".{$methodName}";
        }

        $shortDoc = ucfirst(trim($method));
        $doc = trim($method->doc);
        $doc = str_replace("\n", "\n * ", $doc);

        $methodBody = <<< EOT

/**
 * {$shortDoc}.
 *
 * {$doc}
 *
 * @name {$className}#{$methodName}
 * @function
 * %params
 */

EOT;
        $params = array();
        foreach ($method->field as $field) {
            $fieldName = camelize($field['name'], 1, ' ');

            if ($field['domain']) {
                $xpath = '/amqp/domain[@name="' . $field['domain'] . '"]';
                $domain = $xml->xpath($xpath);
                $type = $domain[0]['type'];
                $doc = trim($domain[0]->doc);
            } else {
                $type = $field['type'];
                $doc = trim($field->doc);
            }
            $doc = str_replace("\n", "\n * ", $doc);
            $type = jstype($type);
            $args[] = "/**{$type}*/{$fieldName}";
            $params[] = "@param {{$type}} {$fieldName} {$doc}";
        }
        $params = implode(nl() . ' * ', $params);
        $methodBody = str_replace('%params', $params, $methodBody);
        $classMethods .= $methodBody;
    }

    if ($events) {
        $classEvents = <<< EOT
//%eventsBody
EOT;
        $eventsBody = array();
        foreach ($events as $event) {
            $eventsBody[] = <<< EOT

/**
 * {$event['short']}.
 *
 * {$event['doc']}
 *
 * @name {$className}#{$event['name']}
 * @event
 * {$event['params']}
 */

EOT;
        }
        $classEvents = str_replace('//%eventsBody', implode('', $eventsBody), $classEvents);
        $eventHandlerBody = str_replace('//%events', $classEvents, $eventHandlerBody);
    } else {
        $eventHandlerBody = '';
    }

    $classBody = str_replace(array('//%methods', '//%events','//%fields'), array($classMethods, $eventHandlerBody, $classFields), $classBody);

    $fp = fopen("../src/api/{$class2}.js", 'w');
    fwrite($fp, $classBody);
    fclose($fp);
}
